package com.company.common.auth.intercepter;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import cn.hutool.http.HttpUtil;
import com.company.cloud.common.auth.annotation.StaffLogc;
import com.company.cloud.common.wrapper.ReadHttpServletRequestWrapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.util.http.MimeHeaders;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.util.StringUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.Feature;
import com.company.cloud.common.auth.annotation.Authc;
import com.company.cloud.common.auth.annotation.Login;
import com.company.cloud.common.bean.AnonyTokenModel;
import com.company.cloud.common.bean.LoginUserInfo;
import com.company.cloud.common.cache.CacheService;
import com.company.cloud.common.constant.BaseReturnEnum;
import com.company.cloud.common.constant.CommonConstant;
import com.company.cloud.common.exception.BusinessException;
import com.company.cloud.common.utils.AuthContextUtils;
import com.company.cloud.common.utils.HttpContextUtils;
import com.company.cloud.common.utils.UA;
import com.company.common.auth.AuthSecurityManager;
import com.company.common.auth.handle.AuthHandle;
import com.company.common.auth.service.AnonyTokenService;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;

public class AuthorizationAnnotationInterceptor extends HandlerInterceptorAdapter {

    protected Logger logger = LoggerFactory.getLogger(getClass());

    private CacheService cacheService;

    private AnonyTokenService anonyTokenService;

    private String staffLogUrl;

    public AuthorizationAnnotationInterceptor(CacheService cacheService, AnonyTokenService anonyTokenService, String staffLogUrl) {
        this.cacheService = cacheService;
        this.anonyTokenService = anonyTokenService;
        this.staffLogUrl = staffLogUrl;
    }

//    @Override
//    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
//        System.out.println("+++++ AuthorizationAnnotationInterceptor : postHandle");
//
//    }

//	@Override
//	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)throws Exception {
//		System.out.println("+++++ AuthorizationAnnotationInterceptor : afterCompletion");
//		super.afterCompletion(request, response, handler, ex);
//	}

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        boolean checkLoginFlag = false;
        String handleName = null;
        if (handler instanceof HandlerMethod) {
            HandlerMethod method = (HandlerMethod) handler;
            Class<?> classType = method.getBeanType();
            if (method.getMethodAnnotation(Login.class) != null || classType.getAnnotation(Login.class) != null) {
                checkLoginFlag = true;
                handleName = AuthHandle.Login;
            } else if (method.getMethodAnnotation(Authc.class) != null
                    || classType.getAnnotation(Authc.class) != null) {
                checkLoginFlag = true;
                handleName = AuthHandle.Authc;
            }
        }

        if (checkLoginFlag == false) {
            handleName = AuthHandle.Anon;
        }

        String token = request.getHeader(CommonConstant.TOKEN_HEADER);
        String anonyToken = request.getHeader(CommonConstant.ANONY_TOKEN_HEADER);
        String ip = HttpContextUtils.getClientIP();

        String url = request.getServletPath();
        LoginUserInfo user = AuthSecurityManager.check(handleName, url, token, cacheService);
        if (user != null) {
//			this.sign(request,user.getKey());
//			request.setAttribute(AuthConstant.REQ_USER_KEY, user);
            user.setIp(ip);
            this.reflectSetparam(request, CommonConstant.USER_HEADER, AuthContextUtils.getUserBase64(user));

            if (handler instanceof HandlerMethod) {
                HandlerMethod method = (HandlerMethod) handler;
                StaffLogc staffLogc = method.getMethodAnnotation(StaffLogc.class);
                if (staffLogc != null) {
                    String title = staffLogc.title();
                    String action = staffLogc.action();
                    String actionAdo = staffLogc.actionAdo();
                    String requestURI = request.getRequestURI();
                    Map<String, String> paramMap = HttpContextUtils.getParamMap();
                    String bodyMap = (String) request.getAttribute("bodyContent");
                    Map<String, Object> body = new HashMap<>();
                    body.put("title", title);
                    body.put("actionUrl", requestURI);
                    body.put("action", action);
                    body.put("actionAdo", actionAdo);
                    body.put("actionParam", JSON.toJSONString(paramMap));
                    body.put("actionBody", bodyMap);
                    body.put("userId", user.getId());
                    logger.info("++++++++++添加操作日志，url: {}, body: {}", staffLogUrl, JSON.toJSONString(body));
                    HttpUtil.post(staffLogUrl, JSON.toJSONString(body));
                }
            }

        } else {
            if (StrUtil.isEmpty(anonyToken)) {
                anonyToken = this.getAnonyToken(request, ip);
                this.reflectSetparam(request, CommonConstant.ANONY_TOKEN_HEADER, anonyToken);
            }
        }

        if (StrUtil.isNotEmpty(anonyToken)) {
            response.addHeader(CommonConstant.ANONY_TOKEN_HEADER, anonyToken);
        }

        return true;
    }


    /**
     * 匿名用户指纹
     */
    private String getAnonyToken(HttpServletRequest request, String ip) {
        String anonyToken = StrUtil.format("ATH{}", IdUtil.fastSimpleUUID());
        if (anonyTokenService != null) {
            UA ua = HttpContextUtils.getUA(request);
            AnonyTokenModel params = new AnonyTokenModel();
            params.setAnonyToken(anonyToken);
            params.setIp(ip);
//			params.setUa(ua);
            anonyTokenService.addAnonyToken(params);
        }
        return anonyToken;
    }

    /**
     * 修改header信息，key-value键值对加入到header中
     */
    private void reflectSetparam(HttpServletRequest request, String key, String value) {
        Class<? extends HttpServletRequest> requestClass = request.getClass();
        try {
            if (request instanceof org.apache.catalina.connector.RequestFacade) {
                Field request1 = requestClass.getDeclaredField("request");
                request1.setAccessible(true);
                Object o = request1.get(request);
                Field coyoteRequest = o.getClass().getDeclaredField("coyoteRequest");
                coyoteRequest.setAccessible(true);
                Object o1 = coyoteRequest.get(o);
                Field headers = o1.getClass().getDeclaredField("headers");
                headers.setAccessible(true);
                MimeHeaders o2 = (MimeHeaders) headers.get(o1);
                o2.addValue(key).setString(value);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /***/
    private void sign(HttpServletRequest request, String key) throws BusinessException {
        if (request instanceof WebHttpServletRequestWrapper) {
            WebHttpServletRequestWrapper requestWrapper = (WebHttpServletRequestWrapper) request;
            String bodyContext = requestWrapper.getBodyContent();
            JSONObject bodyContextObj = JSON.parseObject(bodyContext, Feature.OrderedField);
            String sign = bodyContextObj.getString("sign");
            if (StringUtils.isEmpty(sign)) {
                throw new BusinessException(BaseReturnEnum.SIGN_ERROR);
            }
            bodyContextObj.remove("sign");
            String signBeforeVal = bodyContextObj.toString() + key;
            String signVal = SecureUtil.md5(signBeforeVal).toUpperCase();

            logger.debug("++++ 签名前字符串:{};签名:{}", signBeforeVal, signVal);
            if (!signVal.equals(sign)) {
                throw new BusinessException(BaseReturnEnum.SIGN_ERROR);
            }
        }
    }

}
